//////////////////////////////////////////////////////////////////////
//
//  CryEngine Source code
//	
//	File:Morphing.cpp
//  Implementation of the Morphing class
//
//	History:
//	June 16, 2007: Created by Ivo Herzeg <ivo@crytek.de>
//
//////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include <CryHeaders.h>
#include "FacialAnimation/FacialInstance.h"

#include "Model.h"
#include "CharacterInstance.h"
#include <IJobManager_JobDelegator.h>

void CMorphing::InitMorphing( CCharacterModel* pModel )
{
	m_pModel=pModel;
	m_LinearMorphSequence=-1;
	m_fMorphVertexDrag = 1.0f;
	assert(m_pModel!=0);
}
//-----------------------------------------------------
size_t CMorphing::GetNumMorphs() const
{
  CModelMesh* mesh = m_pModel->GetModelMesh(0);
  return mesh->m_morphTargets.size();
}
//--------------------------------------------------------------------------------
const char* CMorphing::GetMorphName(size_t index) const
{
  CModelMesh* mesh = m_pModel->GetModelMesh(0);
  return mesh->m_morphTargets[index]->m_name.c_str();
}
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
//! Start the specified by parameters morph target
//--------------------------------------------------------------------------------
//--------------------------------------------------------------------------------
void CMorphing::StartMorph (const char* szMorphTarget,const CryCharMorphParams& Params)
{
	RunMorph (szMorphTarget, Params);
}
//! Start the specified by parameters morph target
void CMorphing::StartMorph (int nMorphTargetId, const CryCharMorphParams& params)
{
	RunMorph (nMorphTargetId, params);
}


//! Finds the morph with the given id and sets its relative time.
//! Returns false if the operation can't be performed (no morph)
bool CMorphing::SetMorphTime (const char* szMorphTarget, f32 fTime)
{
	assert(m_pModel!=0);
	CModelMesh* pModelMesh = m_pModel->GetModelMesh(0);
	int32 nMorphTargetId = pModelMesh->FindMorphTarget(szMorphTarget);
	if ( nMorphTargetId<0 )
	{
		AnimWarning("SetMorphTime: Morph Target \"%s\" Not Found for character \"%s\"", szMorphTarget, m_pModel->GetModelFilePath()); 
		return 0;
	}

	CryModEffMorph* pEffector = GetMorphTarget(nMorphTargetId);
	if (pEffector)
	{
		pEffector->setTime (fTime);
		return true;
	}
	return 0;
}


//! Finds the morph with the given id and sets its relative time.
//! Returns false if the operation can't be performed (no morph)
bool CMorphing::SetMorphTime (int nMorphTargetId, float fTime)
{
	CryModEffMorph* pEffector = GetMorphTarget(nMorphTargetId);
	if (pEffector)
	{
		pEffector->setTime (fTime);
		return true;
	}
	return false;
}


void CMorphing::RunMorph (int nMorphTargetId, const CryCharMorphParams&Params)
{
	// find the first free slot in the array of morph target, or create a new one and start morphing there
	unsigned nMorphEffector, nMorphEffectorSize = m_arrMorphEffectors.size();
	for (nMorphEffector = 0; nMorphEffector < nMorphEffectorSize; ++nMorphEffector)
	{
		if (!m_arrMorphEffectors[nMorphEffector].isActive())
		{
			m_arrMorphEffectors[nMorphEffector].StartMorph (nMorphTargetId, Params);
			return;
		}
	}
	m_arrMorphEffectors.resize (m_arrMorphEffectors.size()+1);
	m_arrMorphEffectors.back().StartMorph (nMorphTargetId, Params);
}

int32 CMorphing::RunMorph (const char* szMorphTarget,const CryCharMorphParams&Params,bool bShowNotFoundWarning)
{
	assert(szMorphTarget);

	assert(m_pModel!=0);
	CModelMesh* pSkinning = m_pModel->GetModelMesh(0);
	int32 nMorphTargetId = pSkinning->FindMorphTarget(szMorphTarget);
	if ( nMorphTargetId>=0)
	{
		RunMorph (nMorphTargetId, Params);
		return nMorphTargetId;
	}
	return nMorphTargetId;
}

void CMorphing::UpdateMorphEffectors (float fDeltaTimeSec, const QuatTS& rAnimLocationNext)
{
	//CryAutoLock<CryCriticalSectionNonRecursive> lock(m_lock);
	uint32 numMorphTargets = m_arrMorphEffectors.size();

	for (uint32 nMorphTarget=0; nMorphTarget<numMorphTargets; ++nMorphTarget)
	{
		CryModEffMorph& rMorph = m_arrMorphEffectors[nMorphTarget];
		rMorph.Tick(fDeltaTimeSec);
	}
}


void CMorphing::ClearFinishedMorphEffectors()
{
	uint32 numMorphTargets = m_arrMorphEffectors.size();
	for ( uint32 nMorphTarget = 0; nMorphTarget < numMorphTargets; ++nMorphTarget )
	{
		CryModEffMorph& rMorph = m_arrMorphEffectors[nMorphTarget];
		rMorph.UpdateFinished();
	}

	// clean up the array of morph targets from unused ones
	while ( !m_arrMorphEffectors.empty() && !m_arrMorphEffectors.back().isActive() )
		m_arrMorphEffectors.pop_back();
}


bool CMorphing::StopMorph (const char* szMorphTarget)
{
	assert(m_pModel!=0);
	CModelMesh* pModelMesh = m_pModel->GetModelMesh(0);
	int32 nMorphTargetId = pModelMesh->FindMorphTarget(szMorphTarget);
	unsigned numStopped = 0;
	if (nMorphTargetId>=0) 
	{
		for (;;)
		{
			CryModEffMorph* pEffector = GetMorphTarget(nMorphTargetId);
			if (!pEffector)
				break;
			pEffector->stop();
			++numStopped;
		}
	}

	return numStopped > 0;
}


void CMorphing::StopAllMorphs()
{
	m_arrMorphEffectors.clear();
}


//! Set morph speed scale
//! Finds the morph target with the given id, sets its morphing speed and returns true;
//! if there's no such morph target currently playing, returns false
bool CMorphing::SetMorphSpeed (const char* szMorphTarget, float fSpeed)
{
	CModelMesh* pModelMesh = m_pModel->GetModelMesh(0);
	int32 nMorphTargetId = pModelMesh->FindMorphTarget(szMorphTarget);
	if ( nMorphTargetId>=0 )
	{
		CryModEffMorph* pEffector = GetMorphTarget(nMorphTargetId);
		if (pEffector)
			pEffector->setSpeed (fSpeed);
	}
	return true;
}

//returns the morph target effector, or NULL if no such effector found
CryModEffMorph* CMorphing::GetMorphTarget(int nMorphTargetId)
{
	unsigned nMorphEffectorSize = m_arrMorphEffectors.size();
	for (uint32 nMorphEffector=0; nMorphEffector < nMorphEffectorSize; ++nMorphEffector)
	{
		if (m_arrMorphEffectors[nMorphEffector].getMorphTargetId() == nMorphTargetId)
			return &m_arrMorphEffectors[nMorphEffector];
	}
	return NULL;
}



//! freezes all currently playing morphs at the point they're at
void CMorphing::FreezeAllMorphs()
{
	size_t numMorphEffectors = m_arrMorphEffectors.size();
	for (uint32 i=0; i<numMorphEffectors; i++)
		m_arrMorphEffectors[i].freeze();
}

//Can be called by multiple threads
//Morph lock is acquired externally
void CMorphing::UpdateMorph(std::vector<Vec3>& tempMorphData, int nLOD, int nVerts)
{
	DEFINE_PROFILER_SECTION("CMorphTargetUpdateTask::Execute");

	PIXBeginNamedEvent(0, "Morph Update");

	assert(nVerts);

	if (m_morphTargetsState.size() != nVerts)
	{
		// Make sure num of morph targets vertices are same as in vertex buffer.
		m_morphTargetsState.resize(nVerts);
		memset( &m_morphTargetsState[0],0,nVerts*sizeof(m_morphTargetsState[0]) );
	}

	uint32 numMorphs = m_arrMorphEffectors.size();
	//		float fColor[4] = {0,1,0,1};
	//		g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"numMorphs: %d",numMorphs );	
	//		g_YLine+=16.0f;

	float s = 10.0f * g_pITimer->GetFrameTime()*Console::GetInst().ca_lipsync_vertex_drag;//*m_fMorphVertexDrag;
	
	if (s > 1.0f)
		s = 1.0f;
	if (s < 0.1f)
		s = 0.1f;

	if (numMorphs > 0)
{
		tempMorphData.resize( nVerts );
		Vec3* pTrgMorphData = &tempMorphData[0];
		memset(pTrgMorphData,0,nVerts*sizeof(Vec3));

		for (uint32 nMorphEffector=0; nMorphEffector<numMorphs; ++nMorphEffector)
	{
			const CryModEffMorph& rMorphEffector = m_arrMorphEffectors[nMorphEffector];
			int nMorphTargetId = rMorphEffector.getMorphTargetId ();
			if (nMorphTargetId < 0)
				continue;

			CMorphTarget* pMorphSkin = m_pModel->getMorphSkin(nLOD, nMorphTargetId);
			if (pMorphSkin)
		{
				//const char* morphname = pMorphSkin->m_name;
				//g_pIRenderer->Draw2dLabel( 1,g_YLine, 1.3f, fColor, false,"morphname: %s",morphname );	
				//g_YLine+=16.0f;

				float fBalance = rMorphEffector.m_Params.m_fBalance;
			float fBalanceL = 0;
			float fBalanceR = 0;
			if (fBalance < 0)
				fBalanceL = fabs(fBalance);
			else
				fBalanceR = fabs(fBalance);
			float fBalanceBase = 1.0f - fabs(fBalance);
				if (pMorphSkin)
				{
					float fBlending = rMorphEffector.getBlending();








			float _fBalL = fBlending*fBalanceL;
			float _fBalR = fBlending*fBalanceR;
			float _fBase = fBlending * fBalanceBase;

			f32 ex = pMorphSkin->m_MTExtensions.x/255.0f;
			f32 ey = pMorphSkin->m_MTExtensions.y/255.0f;
			f32 ez = pMorphSkin->m_MTExtensions.z/255.0f;


			uint32 numVerts = pMorphSkin->m_vertices.size();
			Vec3 vUnpackedVertex;

			for (uint32 i=0; i<numVerts; i++)
			{
				uint32 idx = pMorphSkin->m_vertices[i].m_nVertexId;































				Vec3 tmp = pTrgMorphData[idx];
				f32 l=f32(pMorphSkin->m_vertices[i].m_fBalanceLR&0x0f)/15.0f;
				f32 r=f32(pMorphSkin->m_vertices[i].m_fBalanceLR>>0x4)/15.0f; 
				float fVertexBlend = _fBase +  _fBalL*l +  _fBalR*r;

				f32 x=f32(pMorphSkin->m_vertices[i].m_MTVertexX);
				f32 y=f32(pMorphSkin->m_vertices[i].m_MTVertexY);
				f32 z=f32(pMorphSkin->m_vertices[i].m_MTVertexZ);
				vUnpackedVertex.x = x*ex+pMorphSkin->m_MTNegBasis.x;
				vUnpackedVertex.y = y*ey+pMorphSkin->m_MTNegBasis.y;
				vUnpackedVertex.z = z*ez+pMorphSkin->m_MTNegBasis.z;
				tmp += vUnpackedVertex * fVertexBlend;
				pTrgMorphData[idx] = tmp;

			}
				}
			}
		}
		// Slowly scale previous morph target state into the new one.
		if( m_bInstantMorphsApply )
		{
			s = 1.0f;
		}
		for (int32 i = 0; i < nVerts; i++)
		{
			Vec3 vOrig = m_morphTargetsState[i].xyz;
			Vec3 diff = pTrgMorphData[i] - vOrig;
			vOrig += diff*s;
			m_morphTargetsState[i].xyz = vOrig;
		}
	}
	else 
	{
		// Slowly scale previous morph target state into the new one.
		if( m_bInstantMorphsApply )
		{
			s = 1.0f;
		}
		// Slowly scale previous morph target state into the new one.
		for (int32 i = 0; i < nVerts; i++)
		{
			Vec3 vOrig = m_morphTargetsState[i].xyz;
			Vec3 diff = /*pTrgMorphData[i]*/ - vOrig;
			vOrig += diff*s;
			m_morphTargetsState[i].xyz = vOrig;
		}
	}
	
	PIXEndNamedEvent();
}

//Morph lock is acquired externally
void CMorphing::UpdateVBStream(int nVerts, IRenderMesh* pMesh)
{
	PIXBeginNamedEvent(0, "Morph Update VB");

	if (nVerts > 0)
	{
		int32 nStride;
		pMesh->LockForThreadAccess();
		SVF_P3F* pMorphData = (SVF_P3F*)pMesh->GetMorphTargetPtr(nStride, FSL_SYSTEM_CREATE);
		assert(pMorphData);

		// Copy morph targets state into the per pass data.
		cryMemcpy( pMorphData,&m_morphTargetsState[0],nVerts*sizeof(SVF_P3F) );

		pMesh->UnlockStream(VSF_HWSKIN_MORPHTARGET_INFO);
		pMesh->UnLockForThreadAccess();
	}

	PIXEndNamedEvent();
}

#include UNIQUE_VIRTUAL_WRAPPER(IMorphing)
